﻿/// <reference path="../Core.ts" />

module Supernova {

    export class AIHandler {

        public level: number = 0;

        public timeToNextLevel: number = 0;

        public timeToNextSpawn: number = 0;

        public spawners: Array<Spawner> = [];

        public artilleryCreation: { minLevel: number; minPopulation: number; chance: number } = { minLevel: 35, minPopulation: 5, chance: 0.5 };

        private radius: number = 0;

        private unlockedGroups: Array<string> = [];

        private timePerLevel: number = 0;

        private radiusExpandRate: number = 0;

        private timeBetweenSpawners: Array<number> = [0, 0];

        private progressionFactor: number = 0;

        private groups: Array<ISpawnGroup> = [
            {
                name: 'Slow Drone',
                numberToSpawn: [3, 7],
                spawnDelay: [5],
                speed: 2,
                shieldsMultiplier: 0,
                armorMultiplier: 1,
                attackPowerMultiplier: 0,
                attackRadius: 0,
                attackDelay: 0,
                artilleryPods: 0
            },
            {
                name: 'Fast Drone',
                numberToSpawn: [2, 9],
                spawnDelay: [1, 10],
                speed: 12.5,
                shieldsMultiplier: 0,
                armorMultiplier: 1,
                attackPowerMultiplier: 0,
                attackRadius: 0,
                attackDelay: 0,
                artilleryPods: 0
            },
            {
                name: 'Armed I',
                numberToSpawn: [3, 7],
                spawnDelay: [5],
                speed: 2,
                shieldsMultiplier: 0,
                armorMultiplier: 1,
                attackPowerMultiplier: 1,
                attackRadius: 0.3,
                attackDelay: 2,
                artilleryPods: 0
            },
            {
                name: 'Armed II',
                numberToSpawn: [5, 11],
                spawnDelay: [2.5, 7.5],
                speed: 2,
                shieldsMultiplier: 0,
                armorMultiplier: 0.75,
                attackPowerMultiplier: 0.25,
                attackRadius: 1,
                attackDelay: 1,
                artilleryPods: 0
            },
            {
                name: 'Armed III',
                numberToSpawn: [10, 20],
                spawnDelay: [5],
                speed: 2,
                shieldsMultiplier: 0,
                armorMultiplier: 1,
                attackPowerMultiplier: 1,
                attackRadius: 1.25,
                attackDelay: 2,
                artilleryPods: 0
            },
            {
                name: 'Armed IV',
                numberToSpawn: [10, 20],
                spawnDelay: [5],
                speed: 2,
                shieldsMultiplier: 0,
                armorMultiplier: 1.5,
                attackPowerMultiplier: 2,
                attackRadius: 1.5,
                attackDelay: 1,
                artilleryPods: 0
            },
            {
                name: 'Swarm I',
                numberToSpawn: [7, 9],
                spawnDelay: [0.5],
                speed: 5,
                shieldsMultiplier: 0,
                armorMultiplier: 0.25,
                attackPowerMultiplier: 0.25,
                attackRadius: 0.3,
                attackDelay: 1,
                artilleryPods: 0
            },
            {
                name: 'Swarm II',
                numberToSpawn: [9, 17],
                spawnDelay: [0.25],
                speed: 8.5,
                shieldsMultiplier: 0,
                armorMultiplier: 0.1,
                attackPowerMultiplier: 0.2,
                attackRadius: 1,
                attackDelay: 0.5,
                artilleryPods: 0
            },
            {
                name: 'Swarm III',
                numberToSpawn: [13, 25],
                spawnDelay: [0.2],
                speed: 10,
                shieldsMultiplier: 0,
                armorMultiplier: 0.25,
                attackPowerMultiplier: 0.3,
                attackRadius: 1.1,
                attackDelay: 0.5,
                artilleryPods: 0
            },
            {
                name: 'Shielded I',
                numberToSpawn: [3, 7],
                spawnDelay: [4, 6],
                speed: 1,
                shieldsMultiplier: 1,
                armorMultiplier: 0.5,
                attackPowerMultiplier: 0.8,
                attackRadius: 0.3,
                attackDelay: 2,
                artilleryPods: 0
            },
            {
                name: 'Artillery I',
                numberToSpawn: [1],
                spawnDelay: [0],
                speed: 0.5,
                shieldsMultiplier: 0,
                armorMultiplier: 5,
                attackPowerMultiplier: 0.1,
                attackRadius: 1.5,
                attackDelay: 0.25,
                artilleryPods: 1
            },
            {
                name: 'Artillery II',
                numberToSpawn: [1, 2],
                spawnDelay: [5],
                speed: 1,
                shieldsMultiplier: 0,
                armorMultiplier: 5,
                attackPowerMultiplier: 0.25,
                attackRadius: 2,
                attackDelay: 0.1,
                artilleryPods: 2
            },
            {
                name: 'Artillery III',
                numberToSpawn: [3],
                spawnDelay: [5, 20],
                speed: 1.5,
                shieldsMultiplier: 10,
                armorMultiplier: 0.5,
                attackPowerMultiplier: 0.25,
                attackRadius: 2,
                attackDelay: 0.1,
                artilleryPods: 5
            },
            {
                name: 'Dreadnought I',
                numberToSpawn: [1],
                spawnDelay: [0],
                speed: 1,
                shieldsMultiplier: 5,
                armorMultiplier: 6,
                attackPowerMultiplier: 0.2,
                attackRadius: 2.5,
                attackDelay: 0.1,
                artilleryPods: 0
            },
            {
                name: 'Dreadnought II',
                numberToSpawn: [1],
                spawnDelay: [0],
                speed: 2,
                shieldsMultiplier: 10,
                armorMultiplier: 15,
                attackPowerMultiplier: 0.5,
                attackRadius: 2.5,
                attackDelay: 0.05,
                artilleryPods: 10
            },
            {
                name: 'Universal',
                numberToSpawn: [1, 15],
                spawnDelay: [1, 20],
                speed: 2.5,
                shieldsMultiplier: 1,
                armorMultiplier: 1,
                attackPowerMultiplier: 1,
                attackRadius: 1.5,
                attackDelay: 1,
                artilleryPods: 1
            }
        ];

        private levels: Array<{ startLevel: number; unlocksGroups: Array<string>; locksGroups: Array<string> }> = [
            { startLevel: 1, unlocksGroups: ['Slow Drone'], locksGroups: [] },
            { startLevel: 2, unlocksGroups: ['Armed I'], locksGroups: [] },
            { startLevel: 4, unlocksGroups: ['Swarm I'], locksGroups: [] },
            { startLevel: 7, unlocksGroups: ['Armed II', 'Shielded I'], locksGroups: ['Slow Drone'] },
            { startLevel: 10, unlocksGroups: ['Artillery I'], locksGroups: ['Armed I'] },
            { startLevel: 13, unlocksGroups: ['Swarm II'], locksGroups: [] },
            { startLevel: 15, unlocksGroups: ['Armed III'], locksGroups: [] },
            { startLevel: 16, unlocksGroups: [], locksGroups: ['Shielded I'] },
            { startLevel: 17, unlocksGroups: ['Artillery II', 'Fast Drone'], locksGroups: ['Swarm I'] },
            { startLevel: 21, unlocksGroups: [], locksGroups: ['Armed II'] },
            { startLevel: 25, unlocksGroups: ['Dreadnought I'], locksGroups: ['Artillery I'] },
            { startLevel: 28, unlocksGroups: ['Universal'], locksGroups: [] },
            { startLevel: 30, unlocksGroups: ['Swarm III'], locksGroups: [] },
            { startLevel: 34, unlocksGroups: ['Armed IV'], locksGroups: ['Swarm II'] },
            { startLevel: 38, unlocksGroups: ['Artillery III'], locksGroups: [] },
            { startLevel: 40, unlocksGroups: [], locksGroups: ['Armed III'] },
            { startLevel: 45, unlocksGroups: ['Dreadnought II'], locksGroups: ['Dreadnought I'] }
        ];

        constructor(initialWait: number,
                    initialSpawningRadius: number,
                    radiusExpandRate: number,
                    timePerLevel: number,
                    minSpawnInterval: number,
                    maxSpawnInterval: number,
                    progressionFactor: number) {
            this.timeToNextLevel = initialWait;
            this.radius = initialSpawningRadius;
            this.radiusExpandRate = radiusExpandRate;
            this.timePerLevel = timePerLevel;
            this.timeBetweenSpawners = [minSpawnInterval, maxSpawnInterval];
            this.progressionFactor = progressionFactor;
        }

        public tick(secondsElapsed: number) {

            // Handle spawner levels increasing        
            this.timeToNextLevel -= secondsElapsed;
            while (this.timeToNextLevel <= 0) {
                this.advanceToNextLevel();
            }

            // Create new spawners occassionally
            this.radius = Math.min(galaxy.diameter, this.radius + this.radiusExpandRate * secondsElapsed);
            this.timeToNextSpawn -= secondsElapsed;
            if (this.level > 0 && this.timeToNextSpawn <= 0) {
                var spawnIntervalMin = this.timeBetweenSpawners[0];
                var spawnIntervalMax = this.timeBetweenSpawners[1];
                this.timeToNextSpawn = spawnIntervalMin + (1 - this.radius / galaxy.diameter) * (spawnIntervalMax - spawnIntervalMin);
                if (galaxy.population > 0) {
                    var spawner = this.generateSpawner();
                    if (spawner !== null) {
                        this.spawners.push(spawner);
                    }
                }
            }

            // Process existing spawners
            for (var i = 0; i < this.spawners.length; i++) {
                var spawner = this.spawners[i];
                spawner.process(secondsElapsed);
            }
        }

        public advanceToNextLevel() {

            // Advance level number
            this.timeToNextLevel += this.timePerLevel;
            this.level++;

            // Log messages
            console.log('Start of level ' + this.level + '.');
            Notices.addMessage(MessageType.information, 'Start of level ' + this.level + '.');

            // Should we unlock/lock groups?
            for (var i = 0; i < this.levels.length; i++) {
                var details = this.levels[i];
                if (details.startLevel == this.level) {
                    for (var j = 0; j < details.unlocksGroups.length; j++) {
                        var groupName = details.unlocksGroups[j];
                        this.unlockedGroups.push(groupName);
                        console.log('Unlocked spawner group "' + groupName + '".');
                    }
                    for (var j = 0; j < details.locksGroups.length; j++) {
                        var groupName = details.locksGroups[j];
                        this.unlockedGroups.splice(this.unlockedGroups.indexOf(groupName), 1);
                        console.log('Locked spawner group "' + groupName + '".');
                    }
                }
            }

            // Build artillery facility
            if (this.level >= this.artilleryCreation.minLevel && Math.random() <= this.artilleryCreation.chance) {
                var minPopulation = this.artilleryCreation.minPopulation;
                var bodies = findBodies(function (b) { return b.system.owner == OwnerType.computer && b.population >= minPopulation && b.facilities.artillery === null; });
                if (bodies.length > 0) {
                    var body = bodies[randomInt(0, bodies.length - 1)];
                    body.facilities.artillery = new BodyFacilityArtillery();
                }
            }

        }

        private generateSpawner(): Spawner {

            // Select a target system
            var targetSystems = findSystems(function (s) { return s.owner == OwnerType.human; });
            if (targetSystems.length == 0) return null;
            var targetSystem = targetSystems[randomInt(0, targetSystems.length - 1)];

            // Select a source system
            var radius = this.radius;
            var sourceSystems = findSystems(function (s) { return s.owner <= OwnerType.computer && insideCircle(s.x, s.y, targetSystem.x, targetSystem.y, radius); });
            if (sourceSystems.length == 0) return null;
            var sourceSystem = sourceSystems[randomInt(0, sourceSystems.length - 1)];

            // Pick a group
            var groupName = this.unlockedGroups[randomInt(0, this.unlockedGroups.length - 1)];
            var groupIndex = 0;
            var group = this.groups[groupIndex++];
            while (group.name != groupName) {
                group = this.groups[groupIndex++];
            }

            // How many should spawn?
            var minNumberToSpawn = group.numberToSpawn[0];
            var maxNumberToSpawn = group.numberToSpawn.length > 1 ? group.numberToSpawn[1] : minNumberToSpawn;
            var numberToSpawn = randomInt(minNumberToSpawn, maxNumberToSpawn);

            // Calculate base shield/armor/attack strength based on current level number
            var factor = Math.pow(this.level, this.progressionFactor);
            var currentShieldStrength = 0.8 * factor;
            var currentArmorStrength = 1.0 * factor;
            var currentAttackStrength = 0.25 * factor;

            // Create a spawner with appropriate values, based on the selected group
            var minSpawnDelay = group.spawnDelay[0];
            var maxSpawnDelay = group.spawnDelay.length > 1 ? group.spawnDelay[1] : minSpawnDelay;
            var spawner = new Spawner(this.level + ' - ' + group.name, sourceSystem, targetSystem, numberToSpawn, minSpawnDelay, maxSpawnDelay);
            spawner.speed = BASIC_SPEED * group.speed;
            spawner.shields = currentShieldStrength * group.shieldsMultiplier;
            spawner.armor = currentArmorStrength * group.armorMultiplier;
            spawner.attackPower = currentAttackStrength * group.attackPowerMultiplier;
            spawner.attackRadius = group.attackRadius;
            spawner.attackDelay = group.attackDelay;
            spawner.pods = group.artilleryPods;
            console.log('Spawning ' + groupName + ' (x' + numberToSpawn + ').');
            return spawner;
        }

    }

}